home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
filesyst
/
dosfs
/
dmsdosfs.000
/
dmsdosfs
/
dmsdosfs-0.6.9b
/
dmsdos_lfn.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-07-31
|
12KB
|
451 lines
/*
dmsdos_lfn.c
long filename support for dmsdos filesystem
******************************************************************************
DMSDOS (Doublespace/Drivespace compressed MSDOS filesystem) for Linux
written 1995,1996 by Frank Gockel
(C) Copyright 1995,1996 by Frank Gockel
Some code of the dmsdos filesystem has been copied from the msdos filesystem
so there are the following additional copyrights:
(C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
(C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
(C) Copyright 1992-1995 by Linus Torvalds
The DMSDOS filesystem was inspired by the THS filesystem (a simple doublespace
DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
The DMSDOS filesystem is distributed under the Gnu General Public Licence.
See file COPYING for details.
******************************************************************************
*/
#include<linux/fs.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include<linux/msdos_fs.h>
#include<linux/dmsdos_fs.h>
extern Dblsb dblsb[];
/* should be enough */
#define MAX_LFN_DIRENTRIES 25
unsigned char checksum(unsigned char * buf)
{ int i;
unsigned char sum=0;
for(i=0;i<11;++i)
{ if(sum&1)sum=sum/2+0x80;
else sum/=2;
sum+=buf[i];
}
return sum;
}
/* reads lfn direntry: real direntry in buf, filename in longname
if it is a short name, longname is setup right (lower case)
returns ino of short name/error code
if lfn_inos!=NULL: returns list of inos in reverse order (first=short name)
*/
int read_lfn_direntry(struct super_block*sb,int dirstartclust,int cvfnr,
int entrynr, char*longname, unsigned char*buf,
int * lfn_inos)
{ int ino;
unsigned char buf2[32];
char *name;
int islong;
int order,i;
unsigned char checks;
/*printk("DMSDOS: read_lfn_direntry begin\n");*/
/* initialize with zeros -- for safety */
if(lfn_inos)for(i=0;i<MAX_LFN_DIRENTRIES;++i)lfn_inos[i]=0;
islong=dblsb[cvfnr].s_support_lfn&1;
order=0;
name=longname;
*name='\0';
ino=read_dbl_direntry(sb,dirstartclust,cvfnr,entrynr,buf);
if(ino<0)return ino;
if(lfn_inos)lfn_inos[order]=ino;
if(buf[0]==0||buf[0]==0xe5||(buf[11]&ATTR_VOLUME)!=0)return ino;
checks=checksum(buf);
if(entrynr==0)islong=0; /* first can't be long, but does never enter loop */
while(entrynr>0&&order<0x3f&&islong!=0)
{ --entrynr;
++order;
i=read_dbl_direntry(sb,dirstartclust,cvfnr,entrynr,buf2);
if(lfn_inos)lfn_inos[order]=i;
if(checks!=buf2[13]||(buf2[0]&0x3f)!=order||buf2[11]!=0xf)
{ /* if((buf2[0]&0x3f)==order&&buf2[11]==0xf)
printk("DMSDOS: wrong checksum: formula=%d is=%d\n",checks,buf2[13]);
*/
islong=0;
if(lfn_inos)lfn_inos[1]=0; /* cut lfn_inos array (rest after a zero is iqnored) */
break;
}
for(i=0;i<5;++i)
{ if(buf2[1+2*i]==255&&buf2[2+2*i]==255)break;
*(name++)=buf2[1+2*i];
}
for(i=0;i<6;++i)
{ if(buf2[14+2*i]==255&&buf2[15+2*i]==255)break;
*(name++)=buf2[14+2*i];
}
for(i=0;i<2;++i)
{ if(buf2[28+2*i]==255&&buf2[29+2*i]==255)break;
*(name++)=buf2[28+2*i];
}
if(buf2[0]&0x40)break;
}
*name='\0';
if(islong==0)
{ name=longname;
for(i=0;i<8;++i)
{ if(buf[i]==' ')break;
if( (dblsb[cvfnr].s_support_lfn&8)!=0 &&buf[i]>='A'&&buf[i]<='Z')
*(name++)=buf[i]+32;
else *(name++)=buf[i];
}
if(buf[8]!=' ')*(name++)='.';
for(i=8;i<11;++i)
{ if(buf[i]==' ')break;
if( (dblsb[cvfnr].s_support_lfn&8)!=0 &&buf[i]>='A'&&buf[i]<='Z')
*(name++)=buf[i]+32;
else *(name++)=buf[i];
}
*name='\0';
}
/*printk("DMSDOS: read_lfn_direntry: name found %s\n",longname);*/
return ino;
}
/* returns ino */
int scan_dbl_dir_4_lfn(struct super_block*sb, int dirstartclust,
const char*tofind1,int len,int cvfnr,int*lfn_inos)
{ int entrynr,i,ppos;
char name[257];
unsigned char buf[32];
int ino;
char tofind2[257];
const char * tofind;
/*printk("DMSDOS: scan_dbl_dir_4_lfn: searching for %s\n",tofind);*/
if((dblsb[cvfnr].s_support_lfn&1)==0)
{ /* case should not be significant ...
so convert upper case in tofind to lower case
*/
for(i=0;i<len;++i)
{ if(tofind1[i]>='A'&&tofind1[i]<='Z')tofind2[i]=tofind1[i]-'A'+'a';
else tofind2[i]=tofind1[i];
}
/* now match the 8.3 convention */
/* find first '.' */
ppos=0;
for(i=0;i<len;++i)
{ if(tofind2[i]=='.')ppos=i;
}
if(ppos==0&&len>8)len=8;
if(ppos>0&&ppos<=8&&len-ppos>4)len=ppos+4;
if(ppos>8)
{ tofind2[8]='.';
if(len>ppos+1)tofind2[9]=tofind2[ppos+1];
if(len>ppos+2)tofind2[10]=tofind2[ppos+2];
if(len>ppos+3)tofind2[11]=tofind2[ppos+3];
/* subtract number of characters that have been cut off
before the '.' */
len -=(ppos-8);
/* limit total length */
if(len>12)len=12; /* incl. '.' */
}
tofind=tofind2;
}
else tofind=tofind1;
entrynr=0;
do
{ ino=read_lfn_direntry(sb,dirstartclust,cvfnr,entrynr,name,buf,lfn_inos);
if(ino<0)return ino;
if((buf[11]&ATTR_VOLUME)==0&&buf[0]!=0xe5)
{ /*if(strncmp(name,tofind,len)==0)return ino;*/
/*if(strcmp(name,tofind)==0)return ino;*/
if(strlen(name)==len&&strncmp(name,tofind,len)==0)return ino;
}
++entrynr;
}
while(buf[0]!=0);
return -ENOENT;
}
int dmsdos_get_lfn_entry(struct inode *dir, loff_t *pos,unsigned char * buf,
char*longname)
{
int entrynr;
int ino;
int clust;
int cvfnr;
entrynr = (*pos)>>5;
/*printk("DMSDOS: get_lfn_entry nr %d\n",entrynr);*/
*pos += 32;
if((cvfnr=dbltest(dir))!=0)cvfnr=(cvfnr>>DMSDOS_CVFNR_BITS)-1;
else cvfnr=(dir->i_ino>>DMSDOS_CVFNR_BITS)-1;
clust=MSDOS_I(dir)->i_start;/*dbl_inode2startcluster(dir);*/
ino=read_lfn_direntry(dir->i_sb,clust,cvfnr,entrynr,longname,buf,NULL);
/* break on zero entry */
if(buf[0]==0)return -ENOENT;
/*printk("DMSDOS: dmsdos_get_lfn_entry: found %s\n",longname);*/
return ino;
}
/* returns 0 if okay */
int remove_lfn_entry(struct super_block*sb, int dirstartclust,
const char*tofind, int len, int cvfnr)
{ int lfn_inos[MAX_LFN_DIRENTRIES];
int i;
struct buffer_head*bh;
int dblsec,offs;
i=scan_dbl_dir_4_lfn(sb,dirstartclust,tofind,len,cvfnr,lfn_inos);
if(i<0)return i;
i=0;
while(lfn_inos[i]!=0&&i<MAX_LFN_DIRENTRIES)
{ dblsec=(lfn_inos[i]&DMSDOS_ORIGINO_MASK)>>4;
offs=(lfn_inos[i]&0xf)<<5;
bh=read_dbl_sector(sb,dblsec,cvfnr);
if(bh==NULL)return -EIO;
if(i>0&&bh->b_data[offs+11]!=0xf) /* oh oh -- this isn't a long name */
{ bh_free(sb,bh);
break;
}
bh->b_data[offs]=0xe5;
bh->b_data[offs+11]&=~ATTR_VOLUME;
bh_dirty(sb,bh);
bh_free(sb,bh);
++i;
}
return 0;
}
int must_be_long(const char*name,int len,int*p_ppos,int cvn)
{ int ppos;
int i;
/* check point position */
ppos=-1;
for(i=0;i<len;++i)
{ if(name[i]=='.')
{ if(ppos<0)ppos=i;
else
{ if(p_ppos)*p_ppos=ppos;
return 1;
}
}
}
if(p_ppos)*p_ppos=ppos;
if(ppos==-1&&len>8)return 1;
if(ppos==0||ppos>8)return 1;
if(len>12)return 1;
if(ppos==len)return 1;
if(ppos>0&&len-ppos>4) return 1;
/* check for undesired characters */
for(i=0;i<len;++i)
{ if(name[i]<=32)return 1;
if(name[i]==34||name[i]==92)return 1;
if(name[i]>=42&&name[i]<=44)return 1;
if(name[i]>=59&&name[i]<=63)return 1;
if(name[i]==124||name[i]>=126)return 1;
if(name[i]>='a'&&name[i]<='z'&&cvn==0)return 1;
if(name[i]>='A'&&name[i]<='Z'&&cvn!=0)return 1;
}
return 0;
}
/* must be called locked */
/* longname must not already exist (no check here) */
/* returns ino of (incomplete) short entry */
int create_lfn_entry(struct super_block*sb, int dirstartclust,
const char*longname, int len, int cvfnr)
{ /*printk("DMSDOS: create_lfn_entry: Not yet implemented, sorry.\n");
return -EIO;*/
int ilong;
char shortname[12];
int i,j;
int ppos,spos,pos;
char numstr[10];
int versuch;
int lnumstr;
int req;
int lfn_inos[MAX_LFN_DIRENTRIES];
int dblsec;
int offs;
struct buffer_head * bh;
int checks;
int ino;
if(len>255)return -EINVAL;
strcpy(shortname," ");
/* 12345678EXT */
ilong=(dblsb[cvfnr].s_support_lfn&16) ? 1 :
must_be_long(longname,len,&ppos,dblsb[cvfnr].s_support_lfn&8);
if(ilong)strcpy(shortname,"~~~~~~~~ ");
/* 12345678EXT */
if(ppos<=0)ppos=len;
for(i=0;i<ppos&&i<8;++i)
{ shortname[i]=longname[i];
if(longname[i]<=32)shortname[i]='~';
if(longname[i]==34||longname[i]==92)shortname[i]='~';
if(longname[i]>=42&&longname[i]<=44)shortname[i]='~';
if(longname[i]>=59&&longname[i]<=63)shortname[i]='~';
if(longname[i]>=97&&longname[i]<=122)shortname[i]=longname[i]-32;
if(longname[i]==124||longname[i]>=126)shortname[i]='~';
}
if(ppos<len)
{ for(i=ppos+1,j=8;i<len&&j<11;++i,++j)
{ shortname[j]=longname[i];
if(longname[i]<=32)shortname[j]='~';
if(longname[i]==34||longname[i]==92)shortname[j]='~';
if(longname[i]>=42&&longname[i]<=44)shortname[j]='~';
if(longname[i]>=59&&longname[i]<=63)shortname[j]='~';
if(longname[i]>=97&&longname[i]<=122)shortname[j]=longname[i]-32;
if(longname[i]==124||longname[i]>=126)shortname[j]='~';
}
}
if(ilong)
{
versuch=0;
do
{ ++versuch;
sprintf(numstr,"~%d",versuch);
lnumstr=strlen(numstr);
spos=8-lnumstr;
for(i=0;i<lnumstr;++i)shortname[i+spos]=numstr[i];
}
while(scan_dbl_dir_4_filename(sb,dirstartclust,shortname,11,cvfnr)>0);
}
/* okay, everything is set up now */
if(!ilong)req=1;
else req=2+((len-1)/13);
ino=scan_dbl_dir_4_n_empty(sb,dirstartclust,cvfnr,req,lfn_inos);
if(ino<0)return ino;
/* now write the short entry */
dblsec=(ino&DMSDOS_ORIGINO_MASK)>>4;
offs=(ino&0xf)<<5;
bh=read_dbl_sector(sb,dblsec,cvfnr);
if(bh==NULL)return -EIO;
bh->b_data[offs+11]=ATTR_VOLUME; /* ignore this entry as it is incomplete */
for(i=0;i<11;++i)bh->b_data[offs+i]=shortname[i];
bh_dirty(sb,bh);
bh_free(sb,bh);
if(!ilong)return ino;
checks=checksum(shortname);
pos=0;
for(j=1;j<req;++j)
{ dblsec=(lfn_inos[j]&DMSDOS_ORIGINO_MASK)>>4;
offs=(lfn_inos[j]&0xf)<<5;
bh=read_dbl_sector(sb,dblsec,cvfnr);
if(bh==NULL)return -EIO;
bh->b_data[offs]=(j==req-1)?j|0x40:j;
bh->b_data[offs+11]=0xf;
bh->b_data[offs+13]=checks;
bh->b_data[offs+26]=0;
bh->b_data[offs+27]=0;
for(i=0;i<5;++i)
{ if(pos<len)
{ bh->b_data[offs+2+2*i]=0;
bh->b_data[offs+1+2*i]=longname[pos];
}
else if(pos==len)
{ bh->b_data[offs+1+2*i]=0;
bh->b_data[offs+2+2*i]=0;
}
else
{ bh->b_data[offs+1+2*i]=255;
bh->b_data[offs+2+2*i]=255;
}
++pos;
}
for(i=0;i<6;++i)
{ if(pos<len)
{ bh->b_data[offs+15+2*i]=0;
bh->b_data[offs+14+2*i]=longname[pos];
}
else if(pos==len)
{ bh->b_data[offs+14+2*i]=0;
bh->b_data[offs+15+2*i]=0;
}
else
{ bh->b_data[offs+14+2*i]=255;
bh->b_data[offs+15+2*i]=255;
}
++pos;
}
for(i=0;i<2;++i)
{ if(pos<len)
{ bh->b_data[offs+29+2*i]=0;
bh->b_data[offs+28+2*i]=longname[pos];
}
else if(pos==len)
{ bh->b_data[offs+28+2*i]=0;
bh->b_data[offs+29+2*i]=0;
}
else
{ bh->b_data[offs+28+2*i]=255;
bh->b_data[offs+29+2*i]=255;
}
++pos;
}
bh_dirty(sb,bh);
bh_free(sb,bh);
}
return ino;
}